import { describe, expect, test, vi } from 'vitest'

import { getJsonValidator, validateJson } from '@/tests/lib/validate-json-schema'
import readJsonFile from '@/frame/lib/read-json-file'
import { schemaValidator, previewsValidator, upcomingChangesValidator } from '../lib/validator'
import { formatAjvErrors } from '@/tests/helpers/schemas'
import { allVersions } from '@/versions/lib/all-versions'
import { GRAPHQL_DATA_DIR } from '../lib/index'

const allVersionValues = Object.values(allVersions)
const graphqlVersions = allVersionValues.map((v) => v.openApiVersionName)
const graphqlTypes = (readJsonFile('./src/graphql/lib/types.json') as Array<{ kind: string }>).map(
  (t) => t.kind,
)

const previewsValidate = getJsonValidator(previewsValidator)
const upcomingChangesValidate = getJsonValidator(upcomingChangesValidator)

describe('graphql json files', () => {
  vi.setConfig({ testTimeout: 3 * 60 * 1000 })

  // The typeObj is repeated thousands of times in each .json file
  // so use a cache of which we've already validated to speed this
  // test up significantly.
  const typeObjsTested = new Set<string>()
  for (const version of graphqlVersions) {
    const schemaJsonPerVersion = readJsonFile(
      `${GRAPHQL_DATA_DIR}/${version}/schema.json`,
    ) as Record<string, Array<{ kind: string; name: string }>>
    // all graphql types are arrays except for queries
    for (const type of graphqlTypes) {
      test(`${version} schemas object validation for ${type}`, () => {
        for (const typeObj of schemaJsonPerVersion[type]) {
          const key = JSON.stringify(typeObj) + type
          if (typeObjsTested.has(key)) continue
          typeObjsTested.add(key)

          const { isValid, errors } = validateJson(
            schemaValidator[type as keyof typeof schemaValidator],
            typeObj,
          )

          let formattedErrors: any = errors // Can be either raw errors array or formatted string
          if (!isValid) {
            formattedErrors = `kind: ${typeObj.kind}, name: ${typeObj.name}: ${formatAjvErrors(
              errors || [],
            )}`
          }

          expect(isValid, formattedErrors).toBe(true)
        }
      })
    }
  }

  test('previews object validation', () => {
    for (const version of graphqlVersions) {
      const previews = readJsonFile(`${GRAPHQL_DATA_DIR}/${version}/previews.json`) as Array<any> // GraphQL preview schema structure is dynamic
      for (const preview of previews) {
        const isValid = previewsValidate(preview)
        let errors: string | undefined

        if (!isValid) {
          errors = formatAjvErrors(previewsValidate.errors || [])
        }

        expect(isValid, errors).toBe(true)
      }
    }
  })

  test('upcoming changes object validation', () => {
    for (const version of graphqlVersions) {
      const upcomingChanges = readJsonFile(
        `${GRAPHQL_DATA_DIR}/${version}/upcoming-changes.json`,
      ) as Record<string, Array<any>> // GraphQL change object structure is dynamic
      for (const changes of Object.values(upcomingChanges)) {
        // each object value is an array of changes
        for (const changeObj of changes) {
          const isValid = upcomingChangesValidate(changeObj)
          let errors: string | undefined

          if (!isValid) {
            errors = formatAjvErrors(upcomingChangesValidate.errors || [])
          }

          expect(isValid, errors).toBe(true)
        }
      }
    }
  })
})
